package com.anandsoftware.security.impl;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.anandsoftware.security.AbstractSecurityAlgorithm;

public class BlowFish extends AbstractSecurityAlgorithm {

	private String key;
	private String iv;

	/**
	 * @return the key
	 */
	public String getKey() {
		return key;
	}

	/**
	 * @param key
	 *            the key to set
	 */
	public void setKey(String key) {
		this.key = key;
	}

	/**
	 * @return the iv
	 */
	public String getIv() {
		return iv;
	}

	/**
	 * @param iv
	 *            the iv to set
	 */
	public void setIv(String iv) {
		this.iv = iv;
	}

	/**
	 * 
	 * @param plainText
	 *            plaintext to be formatted.
	 * @return returns formatted block of 8 characters in length.
	 */
	private String reFormat(String plainText) {
		String formatedText = plainText;
		if (plainText.length() % 8 > 0) {
			while (formatedText.length() % 8 != 0) {
				formatedText += " ";
			}
		}
		return formatedText;
	}

	/**
	 * @param plainText
	 *            plaintext to encrypt.
	 * @return encrypted string.
	 */
	@Override
	public String encrypt(String plainText) {
		// TODO Auto-generated method stub
		try {
			plainText = reFormat(plainText);
			byte[] IVBytes = getBytes(iv);
			if (IVBytes == null) {
				return null;
			}
			IvParameterSpec IV = new IvParameterSpec(IVBytes);
			// must be the same key we used to encrypt the data
			byte[] strkeyBytes = getBytes(key);
			// byte[] strkeyBytes = strkey.getBytes();
			if (strkeyBytes == null) {
				return null;
			}
			SecretKeySpec key = new SecretKeySpec(strkeyBytes, "Blowfish");
			try {
				Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");
				try {
					cipher.init(Cipher.ENCRYPT_MODE, key, IV);
					try {
						byte[] decrypted = getBytes(plainText);
						if (decrypted == null) {
							return null;
						}
						byte[] encrypted = cipher.doFinal(decrypted);
						return byteArray2Hex(encrypted);
					} catch (Exception e) {
						System.out
								.println("ERROR: encryptBlowfish(dofinal) : to_Encrypt :"
										+ plainText + " : message : " + e);
						e.printStackTrace();
						return null;
					}
				} catch (Exception e) {
					System.out.println("ERROR: encryptBlowfish(init) :" + e);
					e.printStackTrace();
					return null;
				}
			} catch (Exception e) {
				System.out.println("ERROR: encryptBlowfish(getInstance) :" + e);
				e.printStackTrace();
				return null;
			}
		} catch (Exception e) {
			System.out.println("ERROR: encryptBlowfish :" + e);
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @param encryptedText
	 *            encrypted text.
	 * @return returns decrypted text.
	 */
	@Override
	public String decrypt(String encryptedText) {
		// TODO Auto-generated method stub
		try {
			byte[] IVBytes = getBytes(iv);
			if (IVBytes == null) {
				return null;
			}
			IvParameterSpec IV = new IvParameterSpec(IVBytes);
			// must be the same key we used to encrypt the data
			byte[] strkeyBytes = getBytes(key);
			// byte[] strkeyBytes = strkey.getBytes();
			if (strkeyBytes == null) {
				return null;
			}
			SecretKeySpec key = new SecretKeySpec(strkeyBytes, "Blowfish");
			try {
				Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");
				try {
					cipher.init(Cipher.DECRYPT_MODE, key, IV);
					try {
						byte[] encrypted = hex2ByteArray(encryptedText);
						if (encrypted == null) {
							return null;
						}
						byte[] decrypted = cipher.doFinal(encrypted);
						return new String(decrypted);
					} catch (Exception e) {
						System.out
								.println("ERROR: decryptBlowfish(dofinal) : to_decrypt :"
										+ encryptedText + " : message : " + e);
						e.printStackTrace();
						return null;
					}
				} catch (Exception e) {
					System.out.println("ERROR: decryptBlowfish(init) :" + e);
					e.printStackTrace();
					return null;
				}
			} catch (Exception e) {
				System.out.println("ERROR: decryptBlowfish(getInstance) :" + e);
				e.printStackTrace();
				return null;
			}
		} catch (Exception e) {
			System.out.println("ERROR: decryptBlowfish :" + e);
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @param hexString
	 * @return returns string from hex representation
	 */
	@Override
	public String hexToString(String hexString) {
		return hexToString(hexString, 2);
	}

	public String hexToString(String hexString, int groupLength) {
		// TODO Auto-generated method stub
		try {
			StringBuilder sb = new StringBuilder(hexString.length()
					/ groupLength);
			for (int i = 0; i < hexString.length() - groupLength + 1; i += groupLength) {
				String hex = hexString.substring(i, i + groupLength);
				sb.append((char) Integer.parseInt(hex, 16));
			}
			return sb.toString();
		} catch (Exception e) {
			System.out.println("ERROR: hexToString :" + e);
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @param textString
	 * @return hex representation of a string.
	 */
	@Override
	public String stringToHex(String textString) {
		// TODO Auto-generated method stub
		byte ba[] = textString.getBytes();
		StringBuilder str = new StringBuilder();
		for (int i = 0; i < ba.length; i++)
			str.append(String.format("%x", ba[i]));
		return str.toString();
	}

	private byte[] getBytes(String text) {
		try {
			byte[] retVal = new byte[text.length()];
			for (int i = 0; i < text.length(); i++) {
				char anychar = text.charAt(i);
				retVal[i] = (byte) anychar;
			}
			return retVal;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "BlowFish [key=" + key + ", iv=" + iv + "]";
	}

}
